
/**
 * Copyright 2009 cestum.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
package org.waveprotocol.wave.model.document.operation.algorithm.transformer {
import org.goverla.collections.ArrayList;
import org.goverla.collections.HashMap;
import org.waveprotocol.wave.model.document.operation.AnnotationBoundaryMap;
import org.waveprotocol.wave.model.document.operation.DocumentOperationCursor;
import org.waveprotocol.wave.model.document.operation.algorithm.ValueUpdate;
import org.waveprotocol.wave.model.document.operation.impl.AnnotationBoundaryMapImpl;
/**
 * Annotation processor
 **/
public class AnnotationProcessor {

	var cursor:DocumentOperationCursor;
	var active:HashMap= new HashMap();
	var unsynced:HashMap= new HashMap();

	function AnnotationProcessor(cursor:DocumentOperationCursor) {
		this.cursor = cursor;
	}

	function process(map:AnnotationBoundaryMap):void {
		for (var i:int= 0; i < map.endSize(); ++i) {
			var key:String= map.getEndKey(i);
			if (!unsynced.containsKey(key)) {
				unsynced.addItem(key, active.getItem(key));
			}
			active.removeItemAt(key);
		}
		for (var i:int= 0; i < map.changeSize(); ++i) {
			var key:String= map.getChangeKey(i);
			if (!unsynced.containsKey(key)) {
				unsynced.addItem(key, active.getItem(key));
			}
			active.addItem(key, new ValueUpdate(map.getOldValue(i), map
					.getNewValue(i)));
		}
		cursor.annotationBoundary(map);
	}

	function sync():void {
		unsynced.clear();
	}

	function toSynced(toCombine:HashMap):AnnotationBoundaryMap {
		var changeKeys:ArrayList= new ArrayList();
		var changeOldValues:ArrayList= new ArrayList();
		var changeNewValues:ArrayList= new ArrayList();
		for (var key:Object in unsynced.keys) {
			var values:ValueUpdate= ValueUpdate(unsynced.getItem(key));
			if (values == null) {
				var update:ValueUpdate= ValueUpdate(active.getItem(key));
				if (update != null) {
					var forCombining:ValueUpdate= ValueUpdate(toCombine.getItem(key));
					changeKeys.addItem(key);
					changeOldValues
							.addItem((forCombining != null) ? forCombining.oldValue
									: update.newValue);
					changeNewValues.addItem(update.oldValue);
				}
			} else {
				var forCombining1:ValueUpdate= ValueUpdate(toCombine.getItem(key));
				changeKeys.addItem(key);
				if (forCombining1 != null) {
					changeOldValues.addItem(forCombining1.oldValue);
				} else {
					var update1:ValueUpdate= ValueUpdate(active.getItem(key));
					changeOldValues.addItem((update1 != null) ? update1.newValue
							: values.oldValue);
				}
				changeNewValues.addItem(values.newValue);
			}
		}
		return new AnnotationBoundaryMapImpl([], changeKeys.toArray(), changeOldValues.toArray(), changeNewValues.toArray());

	}

	function fromSynced(toCombine:HashMap):AnnotationBoundaryMap {
		var endKeys:ArrayList= new ArrayList();
		var changeKeys:ArrayList= new ArrayList();
		var changeOldValues:ArrayList= new ArrayList();
		var changeNewValues:ArrayList= new ArrayList();
		for (var key:Object in unsynced.keys) {
			var values:ValueUpdate= ValueUpdate(unsynced.getItem(key));
			if (values != null || active.containsKey(key)) {
				var update:ValueUpdate= ValueUpdate(toCombine.getItem(key));
				if (update != null) {
					changeKeys.addItem(key);
					changeOldValues.addItem(update.oldValue);
					changeNewValues.addItem(update.newValue);
				} else {
					endKeys.addItem(key);
				}
			}
		}
		return new AnnotationBoundaryMapImpl(
							endKeys.toArray(), 
							changeKeys.toArray(), 
							changeOldValues.toArray(), 
							changeNewValues.toArray());
	}

}
}